from rest_framework import serializers
from django.db.models import F
from django.conf import settings

# from apps.survey_system.file.models import FileHistory
from .models import QuestionCategory, Question, QuestionOption, Module, \
    Questionnaire, QuestionnaireQsCate, QuestionnaireQuestion, Swiper, ModuleTeam

questionTypeDict = {
    1: '单选题', 2: '多选题', 3: '简答题', 4: '文件上传题'
}


class ModuleSerializer(serializers.ModelSerializer):

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret['full_image_path'] = [{'url': f"{settings.FILE_URL}{file.get('url')}", 'name': file.get('name')}
                                  for file in instance.cover]
        return ret

    class Meta:
        model = Module
        fields = '__all__'
        read_only_fields = ['id', 'name', 'codename']


# 问题分类
class QuestionCategorySerializer(serializers.ModelSerializer):

    class Meta:
        model = QuestionCategory
        fields = '__all__'


class QuestionOptionsSerializer(serializers.ModelSerializer):

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret['question_id'] = instance.question_id
        for i in ret['file']:
            i['full_url'] = f"{settings.FILE_URL}{i['url']}"
            i['file_type'] = f"{i['content_type'].split('/')[0]}" if i['content_type'] else ''
        return ret

    def validate_file(self, value):
        if not value:
            return value
        try:    # 对于有文件的，只取 url 和 name
            new_value = [{'url': i['url'], 'name': i['name'], 'content_type': i['content_type']} for i in value]
            print(new_value)
            return new_value
        except KeyError:
            raise serializers.ValidationError('选项的文件错误')

    class Meta:
        model = QuestionOption
        fields = ['name', 'num', 'file', 'score', 'id', 'is_answer']
        extra_kwargs = {
            'name': {'allow_blank': True},
            'num': {'required': True},
            'file': {'required': True},
            'score': {'required': True},
            'is_answer': {'required': True},
        }


class QuestionCreateSerializer(serializers.ModelSerializer):
    options = serializers.JSONField(write_only=True)
    upload_key = serializers.CharField(write_only=True, required=True)

    def validate(self, attrs):
        if attrs['type'] == 3:  # 简答题不需要验证选项
            attrs.pop('options', None)
            return attrs
        if attrs['type'] == 4:  # 无选项的文件上传题不需要验证选项
            if not attrs['answer_need_option']:
                attrs.pop('options', None)
                return attrs

        if attrs['type'] == 2 and attrs['is_need_limit']:  # 需要限制选项可选个数的多选题，要判断限制个数
            if attrs['limit_choice_count'] <= 1:
                raise serializers.ValidationError(detail=f"选项可选个数需要大于1个")

        tmp = QuestionOptionsSerializer(data=attrs['options'], many=True)
        valid = tmp.is_valid()
        if not valid:
            print(tmp.errors)
            raise serializers.ValidationError(detail=f"选项错误")
        option_data = tmp.validated_data
        for index, data in enumerate(attrs['options']):
            data['file'] = option_data[index]['file']
        num_lst = [i['num'] for i in option_data]
        if len(set(num_lst)) != len(num_lst):
            raise serializers.ValidationError(detail=f"选项编号错误：选项编号不能重复")
        return attrs

    def create(self, validated_data):
        options = validated_data.pop('options', [])
        upload_key = validated_data.pop('upload_key', '')
        obj = Question(**validated_data)
        obj.save()

        # 如果该问题有文件，则将这些文件设置为非冗余文件
        # if validated_data['file']:
        #     FileHistory.objects\
        #         .filter(upload_key=upload_key, path__in=[i['url'] for i in validated_data['file']])\
        #         .update(need_delete=False)

        # 如果有问题选项，则保存
        option_objs = [QuestionOption(question=obj, **data) for data in options]
        QuestionOption.objects.bulk_create(option_objs)
        option_files = []
        for i in options:
            for j in i['file']:
                option_files.append(j['url'])

        # 如果选项有文件，则将这些文件设置为非冗余文件
        # if option_files:
        #     FileHistory.objects \
        #         .filter(upload_key=upload_key, path__in=option_files) \
        #         .update(need_delete=False)

        return obj

    class Meta:
        model = Question
        fields = ('name', 'desc', 'category', 'is_required', 'type', 'options', 'upload_key', 'module', 'file',
                  'answer_need_text', 'answer_need_option', 'score', 'mul_choice_score_type', 'is_need_limit',
                  'limit_choice_count', 'option_type', )


class QuestionListSerializer(serializers.ModelSerializer):
    category_name = serializers.CharField(source='category.name')

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        type_name = questionTypeDict[ret['type']]
        ret['label'] = f"({type_name}) ({ret['score']}分){ret['name']}"
        for i in ret['file']:
            i['full_url'] = f"{settings.FILE_URL}{i['url']}" if 'http' not in i['url'] else i['url']
            i['file_type'] = f"{i['content_type'].split('/')[0]}" if i['content_type'] else ''
        return ret

    class Meta:
        model = Question
        fields = ('id', 'name', 'category', 'is_required', 'type', 'category_name', 'score',
                  'answer_need_text', 'answer_need_option', 'file', 'mul_choice_score_type', 'is_need_limit',
                  'limit_choice_count', 'option_type')


class QuestionUpdateSerializer(serializers.ModelSerializer):
    options = serializers.JSONField(write_only=True)
    upload_key = serializers.CharField(write_only=True, required=True)

    def validate(self, attrs):
        if attrs['type'] not in [1, 2]:
            attrs.pop('options', None)
            return attrs

        tmp = QuestionOptionsSerializer(data=attrs['options'], many=True)
        valid = tmp.is_valid()
        if not valid:
            print(tmp.errors)
            raise serializers.ValidationError(detail=f"选项错误")

        option_data = tmp.validated_data
        num_lst = [i['num'] for i in option_data]
        for index, data in enumerate(attrs['options']):
            data['file'] = option_data[index]['file']
        if len(set(num_lst)) != len(num_lst):
            raise serializers.ValidationError(detail=f"选项编号错误：选项编号不能重复")

        return attrs

    def update(self, instance, validated_data):
        options = validated_data.pop('options', [])
        upload_key = validated_data.pop('upload_key', '')
        origin_type = instance.type     # 原先的题型
        origin_file = instance.file     # 原先的文件
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()

        # 如果本次提交有题目文件，则将这些文件设置为非冗余文件
        # if validated_data['file']:
        #     FileHistory.objects \
        #         .filter(upload_key=upload_key, path__in=[i['url'] for i in validated_data['file']]) \
        #         .update(need_delete=False)
        # 对于本次提交被删除的题目的文件，要设置为冗余文件
        origin_file_url_lst = [i['url'] for i in origin_file]
        new_file_url_lst = [i['url'] for i in validated_data['file']]
        need_delete_file_url = set(origin_file_url_lst) - set(new_file_url_lst)
        # if need_delete_file_url:
        #     FileHistory.objects \
        #         .filter(path__in=[i['url'] for i in validated_data['file']]) \
        #         .update(need_delete=True)

        # 该题目的选项 ----------------------------------------------------------------------------------------------

        need_delete_file_url = []   # 选项里需要设置为冗余的文件
        origin_file_lst = []
        if origin_type in (1, 2):   # 原先为有选项的题目，获取这些选项里的文件
            origin_options_file = instance.options.all().values_list('file', flat=True)
            for file_lst in origin_options_file:
                if not file_lst:
                    continue
                origin_file_lst.extend([i['url'] for i in file_lst])
        # 如果是有选项的题，则要判断选项是删除，修改，还是新增
        if validated_data['type'] in (1, 2):
            option_id_lst = instance.options.all().values_list('id', flat=True)
            need_delete_id_set = set(option_id_lst)
            new_file_url_lst = []   # 本次提交的文件
            will_update, will_create, will_delete = [], [], []
            for data in options:
                for file in data['file']:
                    new_file_url_lst.append(file['url'])
                if 'id' in data:
                    obj = QuestionOption(**data)
                    will_update.append(obj)
                    need_delete_id_set.remove(data['id'])
                else:
                    obj = QuestionOption(question_id=instance.id, **data)
                    will_create.append(obj)
            QuestionOption.objects.bulk_create(will_create)
            QuestionOption.objects.bulk_update(will_update, fields=('name', 'num', 'score', 'file'))
            if need_delete_id_set:
                QuestionOption.objects.filter(id__in=need_delete_id_set).delete()
            # 将本次提交的文件设置为非冗余
            # if new_file_url_lst:
            #     FileHistory.objects.filter(upload_key=upload_key, path__in=new_file_url_lst).update(need_delete=False)
            need_delete_file_url = set(new_file_url_lst) - set(origin_file_lst)
        elif validated_data['type'] == 3 and origin_type in (1, 2):
            # 如果是有选项的题改为简答题，则要删除原先的选项
            instance.options.all().delete()
            need_delete_file_url = origin_file_lst

        # 对于选项里被删除的文件，要设置为冗余文件
        # if need_delete_file_url:
        #     FileHistory.objects \
        #         .filter(path__in=[i['url'] for i in validated_data['file']]) \
        #         .update(need_delete=True)

        return instance

    class Meta:
        model = Question
        fields = ('name', 'desc', 'category', 'is_required', 'type', 'options', 'upload_key', 'module', 'file',
                  'answer_need_text', 'answer_need_option', 'score', 'mul_choice_score_type', 'is_need_limit',
                  'limit_choice_count', 'option_type')


class QuestionnaireListSerializer(serializers.ModelSerializer):
    created_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M", required=False, read_only=True)
    updated_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M", required=False, read_only=True)

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        questionnaire_qs_cnt = self.context.get('questionnaire_qs_cnt', None)
        questionnaire_qs_score = self.context.get('questionnaire_qs_score', None)
        if questionnaire_qs_score is not None:
            ret['questionnaire_score'] = questionnaire_qs_score.get(instance.id, 0)
        if questionnaire_qs_cnt is not None:
            ret['question_cnt'] = questionnaire_qs_cnt.get(instance.id, 0)
        ret['account_name'] = instance.account.realname if instance.account else ''
        return ret

    class Meta:
        model = Questionnaire
        fields = ('id', 'name', 'created_time', 'updated_time')


class QuestionnaireCreateSerializer(serializers.ModelSerializer):
    account = serializers.HiddenField(  # 默认为当前创建者
        default=serializers.CurrentUserDefault()
    )
    typeDataList = serializers.JSONField(write_only=True)

    def validate(self, attrs):
        if not attrs['typeDataList']:
            msg = '需要有分类和题目'
            raise serializers.ValidationError(detail=msg)
        for i in attrs['typeDataList']:
            if len(i['type_data']) == 0:
                msg = '每个分类都需要有题目'
                raise serializers.ValidationError(detail=msg)
        return attrs

    def create(self, validated_data):
        typeDataList = validated_data.pop('typeDataList', [])
        obj = Questionnaire(**validated_data)
        obj.save()

        for data in typeDataList:
            cate_obj = QuestionnaireQsCate.objects.create(module=validated_data['module'], name=data['type_name'],
                                                          questionnaire=obj, sort_num=data['sort_num'])
            qs_bulk_create = []
            for index, qs_obj in enumerate(data['type_data']):
                qs_bulk_create.append(QuestionnaireQuestion(category=cate_obj, question_id=qs_obj['id'], sort_num=index))
            QuestionnaireQuestion.objects.bulk_create(qs_bulk_create)

        return obj

    class Meta:
        model = Questionnaire
        fields = ('id', 'name', 'created_time', 'updated_time', 'typeDataList', 'module', 'account')


class QuestionnaireRetrieveSerializer(serializers.ModelSerializer):

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret['module'] = instance.module_id

        # 找出该问卷的所有分类
        category_qs = instance.category.all().order_by('sort_num', 'id').values('id', 'name')
        category_id_lst = [i['id'] for i in category_qs]
        cate_question_dict = {i['id']: {'type_name': i['name'], 'id': i['id'], 'type_data': []} for i in category_qs}

        # 找出这些分类下的题目
        question_qs = QuestionnaireQuestion.objects\
            .filter(category_id__in=category_id_lst)\
            .order_by('category_id', 'sort_num', 'id')\
            .values('category_id', 'question_id', questionnaire_question_id=F('id'), name=F('question__name'),
                    type=F('question__type'), score=F('question__score'))
        for i in question_qs:
            type_name = questionTypeDict[i['type']]
            i['name'] = f"({type_name}) {i['name']} ({i['score']}分)"
            i['id'] = i['question_id']
            cate_question_dict[i['category_id']]['type_data'].append(i)

        ret['typeDataList'] = []
        for i in category_id_lst:
            ret['typeDataList'].append(cate_question_dict[i])

        return ret

    class Meta:
        model = Questionnaire
        fields = ('id', 'name', )


class QuestionnaireUpdateSerializer(serializers.ModelSerializer):
    typeDataList = serializers.JSONField(write_only=True)

    def validate(self, attrs):
        if not attrs['typeDataList']:
            msg = '需要有分类和题目'
            raise serializers.ValidationError(detail=msg)
        for i in attrs['typeDataList']:
            if len(i['type_data']) == 0:
                msg = '每个分类都需要有题目'
                raise serializers.ValidationError(detail=msg)
        return attrs

    def update(self, instance, validated_data):
        typeDataList = validated_data.pop('typeDataList', [])

        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()

        # 找出原先的问卷的分类
        origin_questionnaire_cate = instance.category.all().values('id', 'name', 'sort_num')
        origin_questionnaire_cate_dict = {i['id']: i for i in origin_questionnaire_cate}
        origin_cate_id_set = set(origin_questionnaire_cate_dict.keys())
        now_cate_id_set = set()
        need_create = []    # 新增的分类
        now_cate = []   # 现有的分类（不确定是否改了分类名和分类下的题目，需要进一步判断）
        for cate in typeDataList:
            cate_id = cate.get('id', None)
            if not cate_id:
                need_create.append(cate)
                continue
            now_cate_id_set.add(cate_id)
            now_cate.append(cate)

        # 判断是否有需要删除的分类
        need_delete = origin_cate_id_set - now_cate_id_set
        if need_delete:
            # 删除了分类，分类下的题目会级联删除
            QuestionnaireQsCate.objects.filter(id__in=need_delete).delete()

        # 判断是否新增了分类
        for data in need_create:
            cate_obj = QuestionnaireQsCate.objects.create(module=instance.module, name=data['type_name'],
                                                          questionnaire=instance)
            qs_bulk_create = []
            for index, qs_obj in enumerate(data['type_data']):
                qs_bulk_create.append(QuestionnaireQuestion(category=cate_obj, question_id=qs_obj['id'],
                                                            sort_num=qs_obj['sort_num']))
            QuestionnaireQuestion.objects.bulk_create(qs_bulk_create)

        # 对于现有的分类，判断是否修改了分类名以及分类下的题目
        for i in now_cate:
            # 修改了分类名
            if i['type_name'] != origin_questionnaire_cate_dict[i['id']]['name']:
                QuestionnaireQsCate.objects.filter(id=i['id']).update(name=i['type_name'])
            # 修改了排序字段
            if i['sort_num'] != origin_questionnaire_cate_dict[i['id']]['sort_num']:
                QuestionnaireQsCate.objects.filter(id=i['id']).update(sort_num=i['sort_num'])
            # 判断该分类下的题目
            origin_question_qs = QuestionnaireQuestion.objects\
                .filter(category_id=i['id'])\
                .values('id', 'question_id', 'sort_num')
            origin_question_dict = {qs['id']: qs for qs in origin_question_qs}
            need_create_data = []
            need_delete_id_lst = set(origin_question_dict.keys())
            need_update = []
            for data in i['type_data']:
                questionnaire_question_id = data.get('questionnaire_question_id', None)
                if not questionnaire_question_id:   # 是新增的题目
                    tmp = {'category_id': i['id'], 'question_id': data['id'], 'sort_num': data['sort_num']}
                    need_create_data.append(QuestionnaireQuestion(**tmp))
                    continue
                need_delete_id_lst.remove(questionnaire_question_id)
                # 判断是否修改了题目的顺序
                if data['sort_num'] != origin_question_dict[questionnaire_question_id]['sort_num']:
                    tmp = {'category_id': i['id'], 'question_id': data['id'], 'sort_num': data['sort_num'],
                           'id': questionnaire_question_id}
                    need_update.append(QuestionnaireQuestion(**tmp))
            if need_delete_id_lst:
                QuestionnaireQuestion.objects.filter(id__in=need_delete_id_lst).delete()
            if need_create_data:
                QuestionnaireQuestion.objects.bulk_create(need_create_data, batch_size=200)
            if need_update:
                QuestionnaireQuestion.objects.bulk_update(need_update, fields=['sort_num'], batch_size=200)

        return instance

    class Meta:
        model = Questionnaire
        fields = ('name', 'updated_time', 'typeDataList',)


class QuestionnaireDetailSerializer(serializers.ModelSerializer):

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret['module'] = instance.module_id

        # 找出该问卷的所有分类
        category_qs = instance.category.all().order_by('sort_num', 'id').values('id', 'name')
        category_id_lst = [i['id'] for i in category_qs]
        cate_question_dict = {i['id']: {'type_name': i['name'], 'id': i['id'], 'type_data': []} for i in category_qs}

        # 找出这些分类下的题目
        question_qs = QuestionnaireQuestion.objects \
            .filter(category_id__in=category_id_lst) \
            .order_by('category_id', 'sort_num', 'id') \
            .values('category_id', 'question_id', option_type=F('question__option_type'),
                    questionnaire_question_id=F('id'), name=F('question__name'),
                    type=F('question__type'), score=F('question__score'), file=F('question__file'))

        question_ids = [i['question_id'] for i in question_qs]
        question_option_qs = QuestionOption.objects.filter(question_id__in=question_ids).values()

        question_children = {i['question_id']: [] for i in question_qs}

        for i in question_option_qs:  # 给每个选项的文件序列化
            i['image'] = []
            i['audio'] = []
            i['video'] = []
            if i['file']:
                for j in i['file']:
                    file_path = j['url']
                    full_file_path = f"{settings.FILE_URL}{file_path}" if 'http' not in file_path else file_path
                    file_type = j['content_type'].split('/')[0]
                    file_type = file_type.lower()
                    if file_type == 'image':
                        i['image'].append(full_file_path)
                    elif file_type == 'audio':
                        i['audio'].append(full_file_path)
                    elif file_type == 'video':
                        i['video'].append(full_file_path)
            question_children[i['question_id']].append(i)  # 方便获取每个问题的选项

        for i in question_qs:  # 给每个选项的文件序列化
            type_name = questionTypeDict[i['type']]
            i['name'] = f"({type_name}) {i['name']} ({i['score']}分)"

            i['image'] = []
            i['audio'] = []
            i['video'] = []
            if i['file']:
                for j in i['file']:
                    file_path = j['url']
                    full_file_path = f"{settings.FILE_URL}{file_path}"
                    file_type = j['content_type'].split('/')[0]
                    file_type = file_type.lower()
                    if file_type == 'image':
                        i['image'].append(full_file_path)
                    elif file_type == 'audio':
                        i['audio'].append(full_file_path)
                    elif file_type == 'video':
                        i['video'].append(full_file_path)
            i['children'] = question_children[i['question_id']]
            cate_question_dict[i['category_id']]['type_data'].append(i)  # 方便获取每个分类的问题

        ret['typeDataList'] = []
        for i in category_id_lst:
            ret['typeDataList'].append(cate_question_dict[i])

        return ret

    class Meta:
        model = Questionnaire
        fields = ('id', 'name', )


class SwiperSerializer(serializers.ModelSerializer):
    created_time = serializers.DateTimeField(format="%Y-%m-%d", required=False, read_only=True)
    updated_time = serializers.DateTimeField(format="%Y-%m-%d", required=False, read_only=True)
    account = serializers.HiddenField(  # 默认为当前创建者
        default=serializers.CurrentUserDefault()
    )

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret['author'] = instance.account.realname if instance.account else ''
        ret['full_image_path'] = [{'url': f"{settings.FILE_URL}{file.get('url')}", 'name': file.get('name')}
                                  for file in instance.cover]
        return ret

    class Meta:
        model = Swiper
        fields = '__all__'


class ModuleTeamSerializer(serializers.ModelSerializer):
    created_time = serializers.DateTimeField(format="%Y-%m-%d", required=False, read_only=True)
    updated_time = serializers.DateTimeField(format="%Y-%m-%d", required=False, read_only=True)

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret['realname'] = instance.account.realname if instance.account else ''
        ret['full_image_path'] = [{'url': f"{settings.FILE_URL}{file.get('url')}", 'name': file.get('name')}
                                  for file in instance.photo]
        ret['account_list'] = [{'id': instance.account.id, 'username': instance.account.username,
                                'realname': instance.account.realname}]
        return ret

    class Meta:
        model = ModuleTeam
        fields = '__all__'
